﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once
#include "GcmpDatabase.h"
#include "GbmpTypeDefs.h"
#include "NdbPropertySchemaType.h"

namespace gcmp
{
    class IIOStream;

    /// 序列化数据的头部。序列化时放在数据前面，描述数据，包括长度和属性。
    class GCMP_DATABASE_EXPORT NdbArchiveBlockHeader
    {
    public:
        NdbArchiveBlockHeader();

        NdbArchiveBlockHeader(UInt32 schemaMajorVersion, UInt32 bodyLength, UInt16 attributes);

        NdbArchiveBlockHeader(UInt32 schemaMajorVersion, UInt64 bodyLength, UInt16 attributes);

        static Int32 GetHeaderLength(UInt32 schemaMajorVersion);

        /// \brief 设置头部所指的数据区域的长度，不包括头部自己
        /// \param bodyLength body长度
        void SetBodyLength(UInt64 bodyLength);

        /// \brief 获取头部所指的数据区域的长度，不包括头部自己
        /// \return body长度
        UInt64 GetBodyLength() const;

        /// \brief 设置所指数据区域的属性
        /// \param attributes 属性
        void SetAttributes(UInt16 attributes) 
        { 
            m_attributes = attributes;
            if (m_schemaMajorVersion >= 6)
                m_bodyLength.Value64Struct.Attribute = attributes;
        }

        /// \brief 获取所指数据区域的属性
        /// \return Int32 属性
        Int32 GetAttributes() const { return m_attributes; }

        Int32& GetAttributesFW() { return m_attributes; }

        void SetElementId_Deprecated(Int64 elementId) { m_elementId = elementId; }

        Int64 GetElementId_Deprecated() const { return m_elementId; }

        Int64& GetElementIdFW_Deprecated() { return m_elementId; }

        Int32 Serialize(IIOStream & ioStream) const;

        static NdbArchiveBlockHeader Unserialize(UInt32 schemaMajorVersion, IIOStream & ioStream, Int32 & length);

    public:
        // 从属性中拿到是否是顶层Element数据，schema类型、visual类型（目前只有部分值类型有），以及visual类型是整型时的base
        static void ParseAttributes(
            UInt16 value,
            NdbPropertySchemaType & schemaType,
            IntOrFloatType & intOrFloatType,
            IntBaseType & intBaseType
        );

        // 将是否是顶层Element数据，schema类型、visual类型，以及visual类型是整型时的base组合成属性值
        static UInt16 CombineAttributes(
            NdbPropertySchemaType schemaType,
            IntOrFloatType intOrFloatType,
            IntBaseType intBaseType
        );

    private:
        /// \brief 设置头部所指的数据区域的长度，不包括头部自己
        /// \param bodyLength 32位的body长度
        void SetBodyLength32(UInt32 bodyLength);

        /// \brief 设置头部所指的数据区域的长度，不包括头部自己
        /// \param bodyLength 64位的body长度
        void SetBodyLength64(UInt64 bodyLength);

        /// \brief 获取头部所指的数据区域的长度，不包括头部自己
        /// \return UInt32 32位的body长度
        UInt32 GetBodyLength32() const;

        /// \brief 获取头部所指的数据区域的长度，不包括头部自己
        /// \return UInt32 32位的body长度
        UInt64 GetBodyLength64() const;

    private:
        typedef struct {            
            UInt64 BodyLength : 48;           /// < body长度
            UInt64 Attribute  : 16;           /// < 文件头属性值
        } BodyLenStruct;

        union VariableBodyLength
        {
            UInt32 Value32;
            UInt64 Value64;
            BodyLenStruct Value64Struct;
        };

        UInt32 m_schemaMajorVersion;    // 版本号，决定了m_bodyLength是32位还是64位

        VariableBodyLength m_bodyLength;// 序列化数据的大小
        Int32 m_attributes;             // 序列化数据的属性。
        Int64 m_elementId;              // ElementId，-1表示数据块不是Element的数据块。
    };

    /// 容器类型序列化数据的头部。
    class GCMP_DATABASE_EXPORT NdbContainerArchiveBlockHeader
    {
    public:
        /// 版本
        static const UInt8 s_CurrentVersion = 1;

        /// 从版本、元素数量、容器类型来构造
        NdbContainerArchiveBlockHeader(UInt8 version, UInt64 size, NdbPropertySchemaType containerType);

        /// \brief 返回版本，范围[0,4)
        /// \return 版本号
        UInt8 GetVersion() const { return m_info.Version; }

        /// \brief 返回元素的数量，范围[0,2^48)
        /// \return 元素数量
        UInt64 GetSize() const { return m_info.Size; }

        /// \brief 是否共享元素的头部
        /// \return 是否共享
        bool IsHeaderShared() const { return 1 == m_info.IsHeaderShared; }

        /// \brief 获取容器类型，范围[0,2)
        /// \return 0-序列式容器；1-关联式容器
        UInt8 GetType() const { return m_info.Type; }

        /// \brief 设置元素共享的Header
        ///
        /// \param header 元素的Header
        /// \param index 索引，范围是[0,2)
        /// \return 是否设置成功。比如给出的index超出范围，则返回失败。
        bool SetSharedHeader(const NdbArchiveBlockHeader & header, int index);

        /// \brief 返回元素共享的Header指针
        ///
        /// \return 元素共享的Header指针
        NdbArchiveBlockHeader* GetSharedHeader();

        /// \brief 序列化
        /// \param ioStream 序列化的目标流
        /// \return 序列化花费的字节数
        UInt32 Serialize(IIOStream & ioStream) const;

        /// \brief 反序列化
        /// \param schemaMajorVersion 文件格式版本号
        /// \param ioStream 反序列化的源流
        /// \param length 消耗的字节数
        /// \param isHeaderShared 是否共享Header
        /// \return 返回容器类型序列化数据的头部
        static NdbContainerArchiveBlockHeader Unserialize(UInt32 schemaMajorVersion, IIOStream & ioStream, Int32 & length, bool & isHeaderShared);

    private:
        /// 属性和元素长度信息。布局如下：
        ///
        ///    高16位                                                                             低48位
        ///    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
        ///    | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 | 55 | 54 | 53 | 52 | 51 | 50 | 49 | 48 | 47 - 0            |
        ///    +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
        ///    |版本号   |共享|类型|保留                                                       |元素数量           |
        typedef struct {
            UInt64 Size : 48;           /// < 容器大小
            UInt64 /*Reserved*/ : 12;   /// < 保留
            UInt64 Type : 1;            /// < 容器类型
            UInt64 IsHeaderShared : 1;  /// < 是否共享元素的头部
            UInt64 Version : 2;         /// < 格式版本
        } Info;
        Info m_info;

        /// 直接从info值构造。
        NdbContainerArchiveBlockHeader(UInt64 infoValue);

        /// 0到2个元素共享的Header
        NdbArchiveBlockHeader m_sharedHeaders[2];
    };

    template <class T>
    static NdbArchiveBlockHeader GetSharedBlockHeader()
    {
        NdbArchiveBlockHeader blockHeader;
        blockHeader.SetBodyLength(sizeof(T));

        NdbPropertySchemaType propertySchemaType = NdbPropertySchemaTypeTrait<T>::PropertySchemaType;
        IntBaseType intBaseType = NdbPropertySchemaTypeTrait<T>::IntBaseType;
        IntOrFloatType intOrFloatType = NdbPropertySchemaTypeTrait<T>::IntOrFloatType;
        UInt16 attributes = NdbArchiveBlockHeader::CombineAttributes(propertySchemaType, intOrFloatType, intBaseType);
        blockHeader.SetAttributes(attributes);
        return blockHeader;
    }
}
